package com.star.star.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.star.common.enums.AuditStatus;
import com.star.mentor.service.MentorEvaluationService;
import com.star.star.contants.StarRedis;
import com.star.star.dto.*;
import com.star.star.entity.Star;
import com.star.star.entity.StarArt;
import com.star.star.entity.StarArtEmpower;
import com.star.star.entity.StarArtLog;
import com.star.star.enums.ArtLogType;
import com.star.star.enums.ArtSource;
import com.star.star.mapper.StarArtMapper;
import com.star.star.service.*;
import com.star.star.struct.StarArtStruct;
import com.star.star.vo.*;
import org.aspectj.weaver.ast.Var;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 艺人作品 服务实现类
 * </p>
 *
 * @author yangzj
 */
@Service
public class StarArtServiceImpl extends ServiceImpl<StarArtMapper, StarArt> implements StarArtService {

    @Autowired
    private StarService starService;
    @Autowired
    private StarAlbumArtService starAlbumArtService;
    @Autowired
    private RedissonClient redisson;
    @Autowired
    private MentorEvaluationService mentorEvaluationService;
    @Autowired
    private StarArtLogService starArtLogService;
    @Autowired
    private StarArtEmpowerService starArtEmpowerService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void auditBatch(List<AuditDTO> arts, Long starId) {
        if (CollUtil.isEmpty(arts)) {
            return;
        }
        List<StarArt> collect = arts.stream().map(c -> new StarArt().setId(c.getId())
                .setAuditStatus(c.getAuditStatus())
                .setAuditReason(c.getAuditReason())
                .setAuditTime(LocalDateTime.now())).collect(Collectors.toList());
        this.updateBatchById(collect);
        // 作品审核通过后分配导师
        List<StarArt> starArts = this.listByIds(collect.stream().filter(c -> AuditStatus.OK.getCode().equals(c.getAuditStatus())).map(StarArt::getId).collect(Collectors.toList()));
        mentorEvaluationService.allocation(starId, starArts.stream().map(c -> c.getStyle().toString()).collect(Collectors.toList()));
    }

    @Override
    public void updatePublish(Long id, Boolean isPublish) {
        StarArt starArt = Assert.notNull(this.getById(id), "作品不存在");
        if (isPublish) {
            // 作品上架满足两个条件，1、作品关联歌手，必须为签约歌手，2、作品版权
            Assert.isTrue(starArt.getIsEmpower(), "作品授权后，才能上架");
            Assert.isTrue(starService.isSigning(starArt.getStarId()), "艺人签约后，作品才能上架");
        }
        this.updateById(new StarArt().setId(id).setIsPublish(isPublish).setPublishTime(LocalDateTime.now()));
        // 记录变更日志
        Function<Boolean, String> fun = (b) -> b ? "上架" : "下架";
        Boolean oldIsPublish = starArt.getIsPublish();
        StarArtLog log = new StarArtLog().setArtId(id).setType(ArtLogType.PUBLISH.getCode())
                .setOldData(fun.apply(oldIsPublish)).setNewData(fun.apply(isPublish));
        starArtLogService.writeBatch(Collections.singletonList(log));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateBatchEmpower(List<StarArt> arts, boolean isSchedule) {
        if (CollUtil.isEmpty(arts)) {
            return;

        }
        List<Long> artIds = arts.stream().map(StarArt::getId).collect(Collectors.toList());
        List<StarArt> oldArts = this.listByIds(artIds);
        this.updateBatchById(arts);
        // 记录变更日志
        Map<Long, StarArt> newMap = arts.stream().collect(Collectors.toMap(StarArt::getId, c -> c));
        // 定时任务时没有用户信息， 取授权的创建人
        Map<Long, Long> empowerMap = isSchedule ? starArtEmpowerService.listByArtIds(artIds).stream().collect(Collectors.toMap(StarArtEmpower::getArtId, c -> c.getCreator()))
                : new HashMap<>();
        Function<Boolean, String> fun = (b) -> b ? "已授权" : "未授权";
        List<StarArtLog> logs = new ArrayList<>();
        oldArts.forEach(c -> {
            Boolean oldIsEmpower = c.getIsEmpower();
            Boolean newIsEmpower = newMap.get(c.getId()).getIsEmpower();
            if (!oldIsEmpower.equals(newIsEmpower)) {
                logs.add(new StarArtLog().setArtId(c.getId()).setType(ArtLogType.EMPOWER.getCode())
                        .setOldData(fun.apply(oldIsEmpower)).setNewData(fun.apply(newIsEmpower))
                        .setCreator(isSchedule ? empowerMap.get(c.getId()) : null));
            }
        });
        starArtLogService.writeBatch(logs);
    }

    @Override
    public IPage<StarArtPageVO> page(StarArtPageDTO dto) {
        return baseMapper.page(dto);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveWxuser(StarArtSaveWxuserDTO dto, Long userId) {
        Star star = Assert.notNull(starService.getByUserId(userId), "您还不是艺人");
        this.check(dto);
        this.check(star.getId(), userId);
        StarArt starArt = StarArtStruct.INSTANCE.to(dto)
                .setStarId(star.getId())
                .setFileDuration(this.getFileDuration(dto.getFileUrl()))
                .setAuditStatus(AuditStatus.WAIT.getCode())
                .setIsEmpower(false).setIsPublish(false)
                .setCollectCount(0).setLikeCount(0).setPlayCount(0)
                .setSource(ArtSource.DEFAULT.getCode());
        this.save(starArt);
        // 保存专辑关联作品
        Long id = starArt.getId();
        starAlbumArtService.saveWxuser(dto.getIsAlbum() ? dto.getAlbumId() : null, id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateWxuser(StarArtUpdateWxuserDTO dto, Long userId) {
        Star star = Assert.notNull(starService.getByUserId(userId), "您还不是艺人");
        this.check(dto);
        this.check(star.getId(), userId);
        Long id = dto.getId();
        StarArt old = Assert.notNull(this.getById(id), "艺人作品不存在");
        LambdaUpdateWrapper<StarArt> wrapper = Wrappers.<StarArt>lambdaUpdate()
                .eq(StarArt::getId, id);
        // 变更文件地址重新审核
        if (!Objects.equals(old.getFileUrl(), dto.getFileUrl())) {
            wrapper.set(StarArt::getAuditStatus, AuditStatus.WAIT.getCode())
                    .set(StarArt::getAuditReason, null)
                    .set(StarArt::getAuditTime, null);
        }
        StarArt starArt = StarArtStruct.INSTANCE.to(dto);
        this.update(starArt, wrapper.set(StarArt::getStyle, dto.getStyle())
                .set(StarArt::getCoverUrl, dto.getCoverUrl())
                .set(StarArt::getDescription, dto.getDescription())
                .set(StarArt::getRemark, dto.getRemark()));
        // 更新专辑关联作品
        starAlbumArtService.updateWxuser(dto.getIsAlbum() ? dto.getAlbumId() : null, id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateBatchIsAlbumWxuser(List<Long> ids, boolean isAlbum) {
        this.updateBatchById(ids.stream().map(c -> new StarArt().setId(c).setIsAlbum(isAlbum)).collect(Collectors.toList()));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteByIdWxuser(Long id, Long userId) {
        StarArt starArt = Assert.notNull(this.getById(id), "作品不存在");
        this.check(starArt.getStarId(), userId);
        this.removeById(id);
        // 删除专辑关联作品
        starAlbumArtService.deleteWxuserByArtId(id);
    }

    @Override
    public StarArtDetailWxuserVO getDetailWxuser(Long id, Long userId) {
        return baseMapper.getDetailWxuser(id, userId);
    }

    @Override
    public IPage<StarArtPageWxuserVO> pageWxuser(StarArtPageWxuserDTO dto) {
        return baseMapper.pageWxuser(dto);
    }

    @Override
    public StarArtCountWxuserVO countWxuser(Long userId) {
        return baseMapper.countWxuser(userId);
    }

    @Override
    public void playCount(Long id, Long userId) {
        // 一个人12小时算一次
        RBucket<Object> bucket = redisson.getBucket(StarRedis.ART_PLAY + id + "_" + userId);
        if (bucket.isExists()) {
            return;
        }
        bucket.set(true, 12, TimeUnit.HOURS);
        // 保证播放量正确
        RAtomicLong atomicLong = redisson.getAtomicLong(StarRedis.ART_PLAY_COUNT + id);
        long play;
        if (atomicLong.get() == 0) {
            RLock lock = redisson.getLock(StarRedis.ART_PLAY_LOCK + id);
            try {
                lock.lock();
                StarArt starArt = Assert.notNull(this.getById(id), "作品不存在");
                play = starArt.getPlayCount() + 1;
                atomicLong.set(play);
                atomicLong.expire(Duration.ofHours(48));
            } finally {
                lock.unlock();
            }
        } else {
            play = atomicLong.incrementAndGet();
        }
        // 如果点赞多了，一直更新主表不好，以后可以更新几次量/定时更新主表或独立个表
        this.updateById(new StarArt().setId(id).setPlayCount((int) play));
    }

    @Override
    public List<StarArt> listPlayTop(List<Long> starIds) {
        return baseMapper.listPlayTop(starIds);
    }

    @Override
    public List<StarArtListVO> listByStarId(Long starId, Boolean isPublish, Integer auditStatus, Boolean isEmpower) {
        return baseMapper.listByStarId(starId, isPublish, auditStatus, isEmpower);
    }

    @Override
    public void incrementCollectCount(Long id, boolean increment) {
        StarArt starArt = Assert.notNull(this.getById(id), "作品不存在");
        int count = increment ? starArt.getCollectCount() + 1 : starArt.getCollectCount() - 1;
        this.updateById(new StarArt().setId(id).setCollectCount(count));
    }

    @Override
    public void incrementLikeCount(Long id, boolean increment) {
        StarArt starArt = Assert.notNull(this.getById(id), "作品不存在");
        int count = increment ? starArt.getLikeCount() + 1 : starArt.getLikeCount() - 1;
        this.updateById(new StarArt().setId(id).setLikeCount(count));
    }

    /**
     * 检查
     *
     * @param dto 入参
     */
    private void check(StarArtSaveWxuserDTO dto) {
        if (dto.getIsAlbum()) {
            Assert.isFalse(dto.getAlbumId() == null, "专辑ID不能为空");
        }
    }

    /**
     * 检查
     *
     * @param starId 明星ID
     * @param userId 用户ID
     */
    private void check(Long starId, Long userId) {
        Assert.isTrue(starService.exists(starId, userId), "艺人专辑不存在");
    }

    /**
     * 获取文件时长秒
     *
     * @param fileUrl 文件url
     * @return int
     */
    private int getFileDuration(String fileUrl) {
        // todo
        /// int seconds = MediaUtil.getSeconds(fileUrl);
        return 0;
    }
}
